﻿#pragma once
#include <cstddef>
#include "conditions.h"
#include "core/value.h"
#include "core/typedefs.h"

#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif

namespace Commands
{

class IDispatchable;

/** \anchor GetData
\brief Класс вычисляемого значения, получающего данные из диспетчера
\details Данные (float) хранятся в ассоциативном массиве в диспетчере */
class GetData : public Core::Value
{
public:
    GetData(IDispatchable* dispatcher, const int dataId);

    GetData(IDispatchable* dispatcher, const int queueId, const int dataId);

    virtual ~GetData();

private:
    friend class Core::Inspector;

    virtual void Accept(Core::Inspector& inspector);

    GetData();

    virtual GetData* Copy(void) const;

    virtual float Execute(const float seconds);

    virtual bool IsFinished(void) const;


    IDispatchable* const dispatchable;
    const int dataId;   /// inspect
    const int queueId;  /// inspect
    bool queueDefined;
};
//-----------------------------------------------------------------------------
/** \anchor Add
\brief Класс вычисляемого значения, складывающего значения своих аргументов */
class Add : public Core::Value
{
public:
    Add(float first, float second);

    Add(const Core::Value& first, const Core::Value& second);

    Add(const Core::Value& first, const float second);

    Add(const float first, const Core::Value& second);

    virtual ~Add();

private:
    friend class Core::Inspector;

    virtual void Accept(Core::Inspector& inspector);

    Add(){}

    virtual Add* Copy(void) const;

    virtual float Execute(const float seconds);

    virtual bool IsFinished(void) const;


    Core::Value* first;     /// inspect
    Core::Value* second;    /// inspect
};
//-----------------------------------------------------------------------------
/** \anchor KinematicLaw
\brief Базовый вспомогательный класс определения параметров закона изменения величины
в зависимости от времени */
class KinematicLaw
{
public:
    virtual ~KinematicLaw(){}
    inline float GetStart(void) const {return start;}
    inline float GetEnd(void) const {return end;}
    inline float GetSpeed(void) const {return speed;}
    inline float GetAcceleration(void) const {return acceleration;}
    inline float GetTime(void) const {return time;}

protected:
    KinematicLaw()
    : start(0.0)
    , end(0.0)
    , speed(0.0)
    , acceleration(0.0)
    , time(0.0)
    {}

    float start;
    float end;
    float speed;
    float acceleration;
    float time;
};
//-----------------------------------------------------------------------------
/** \anchor Uniform
\brief Вспомогательный класс определения параметров закона изменения величины
в зависимости от времени при равномерном изменении от начального до конечного значения */
class Uniform : public KinematicLaw
{
public:
    enum Parameter
    {
        START,  /**< Расчет начального значения вычисляемой величины */
        SPEED,  /**< Расчет скорости изменения значения вычисляемой величины */
        END     /**< Расчет конечного значения вычисляемой величины */
    };

    Uniform(float start, float speed);
    Uniform(float start, float speed, float end);
    Uniform(float start, float speed, float end, float time, Parameter parameter);
};
//-----------------------------------------------------------------------------
/** \anchor Accelerated
\brief Вспомогательный класс определения параметров закона изменения величины
в зависимости от времени при ускоренном изменении от начального до конечного значения */
class Accelerated : public KinematicLaw
{
public:
    enum Parameter
    {
        START,          /**< Расчет начального значения вычисляемой величины */
        SPEED,          /**< Расчет скорости изменения значения вычисляемой величины */
        ACCELERATION,   /**< Расчет ускорения изменения значения вычисляемой величины */
        END             /**< Расчет конечного значения вычисляемой величины */
    };

    Accelerated(float start, float speed, float acceleration);
    Accelerated(float start, float speed, float acceleration, float end);
    Accelerated(float start, float speed, float acceleration, float end, float time, Parameter parameter);
};
//-----------------------------------------------------------------------------
/** \anchor Decelerated
\brief Вспомогательный класс определения параметров закона изменения величины
в зависимости от времени при замедляющемся изменении от начального до конечного значения
\details В конечной точке изменения величины мгновенная скорость изменения равна нулю.
Этот закон необходим когда нужно проэмулировать замедление каких-то процессов */
class Decelerated : public KinematicLaw
{
public:
    enum Parameter
    {
        START,          /**< Расчет начального значения вычисляемой величины */
        SPEED,          /**< Расчет скорости изменения значения вычисляемой величины */
        DECELERATION,   /**< Расчет замедления скорости изменения значения вычисляемой величины */
        END             /**< Расчет конечного значения вычисляемой величины */
    };

    Decelerated(float start, float acceleration, float end);
    Decelerated(float start, float speed, float acceleration, float end, Parameter parameter);
};
//-----------------------------------------------------------------------------
/** \anchor ChangeValue
\brief Класс вычисляемого значения изменение которого происходит равномерно, равноускоренно или равнозамедленно.
\details Самое используемое вычисляемое значение. Является параметром других вычисляемых значений
или собственно команд */
class ChangeValue : public Core::Value
{
public:
    /** \anchor ChangeValue1
    \brief Изменение вычисляемой величины в рамках переданного закона
    \param law - закон изменения величины
    \sa Uniform \sa Accelerated \sa Decelerated */
    ChangeValue(const KinematicLaw& law);

    /** \anchor ChangeValue2
    \brief Изменение величины равномерно до достижения предела значения float
    \details Конечное значение вычисляется на основании параметров команды
    \param start - начальное значение изменяемой величины
    \param speed - скорость изменения величины (значений в секунду)
    \deprecated Следует использовать ChangeValue, принимающую один из законов изменения величный
    \sa Uniform \sa Accelerated \sa Decelerated */
    DEPRECATED(ChangeValue(float start, float speed));

    /** \anchor ChangeValue3
    \brief Изменение величины равномерно до достижения конечного значения
    \param start - начальное значение изменяемой величины
    \param speed - скорость изменения величины (значений в секунду)
    \param end - конечное значение вычисляемой величины
    \deprecated Следует использовать ChangeValue, принимающую один из законов изменения величный
    \sa Uniform \sa Accelerated \sa Decelerated */
    DEPRECATED(ChangeValue(float start, float speed, float end));

    /** \anchor ChangeValue4
    \brief Изменение величины равноускоренно до достижения конечного значения
    \param start - начальное значение изменяемой величины
    \param speed - скорость изменения величины (значений в секунду)
    \param acceleration - ускорение изменения величины (значений в секунду ^ 2)
    \param end - конечное значение вычисляемой величины
    \deprecated Следует использовать ChangeValue, принимающую один из законов изменения величный
    \sa Uniform \sa Accelerated \sa Decelerated */
    DEPRECATED(ChangeValue(float start, float speed, float acceleration, float end));

    /** \brief Перечисление парамеров кторые можно рассчитать автоматичеки в команде \ref ChangeValue6
    \deprecated Следует использовать ChangeValue, принимающую один из законов изменения величный
    \sa Uniform \sa Accelerated \sa Decelerated */
    enum Parameter
    {
        NOPARAMETER,    /**< Не расчитываь автоматический параметр (значение по-умолчанию) */
        START,          /**< Расчет начального значения вычисляемой величины */
        END,            /**< Расчет конечного значения вычисляемой величины */
        SPEED,          /**< Расчет скорости изменения значения вычисляемой величины */
        ACCELERATION    /**< Расчет ускорения изменения значения вычисляемой величины */
    };

    /** \anchor ChangeValue6
    \brief Изменение величины равноускоренно до достижения конечного значения за указаное время
    \details Автоматически может рассчитаться на базе остальных данных любой из первых четырех
    параметров. Тогда указываемое его значение роли не играет - можно указывать 0.0f
    \param start - начальное значение изменяемой величины
    \param speed - скорость изменения величины (значений в секунду)
    \param acceleration - ускорение изменения величины (значений в секунду ^ 2)
    \param end - конечное значение вычисляемой величины
    \param time - время, за которое изменяемая величина должна достигнуть конечное значение
    \param Parameter - параметр, который нужно рассчитать автоматически
    \deprecated Следует использовать ChangeValue, принимающую один из законов изменения величный
    \sa Uniform \sa Accelerated \sa Decelerated */
    DEPRECATED(ChangeValue(float start, float speed, float acceleration, float end, float time, Parameter parameter));

    virtual ~ChangeValue();

private:
    friend class Core::Inspector;

    virtual void Accept(Core::Inspector& inspector);

    ChangeValue(){}

    virtual ChangeValue* Copy(void) const;

    virtual float Execute(const float seconds);

    virtual bool IsFinished(void) const;

    DEPRECATED(void Init(void));

    float start;
    float end;
    float time;
    float speed;
    float acceleration;
    Parameter parameter;

    float elapsedTime;

    bool isFirstCall;
    bool isFinished;
};
//-----------------------------------------------------------------------------
/** \anchor Delta
\brief Класс изменяемого значения,вычисляющего <b>изменение</b> аргумента по отношению
к предыдущему его значению */
class Delta : public Core::Value
{
public:
    Delta(const Core::Value& variable);

    virtual ~Delta();

private:
    friend class Core::Inspector;

    virtual void Accept(Core::Inspector& inspector);

    Delta(){}

    virtual Delta* Copy(void) const;

    virtual float Execute(const float seconds);

    virtual bool IsFinished(void) const;

    Core::Value* variable;  /// inspect
    float previousValue;
};
//-----------------------------------------------------------------------------
/** \brief Класс изменяемого значения, изменение которого происходит по закону синуса
\anchor Sine */
class Sine : public Core::Value
{
public:
    Sine(float angle);

    Sine(const Core::Value& angle);

    Sine(const Core::Value& angle, const float magnitude);

    Sine(const Core::Value& angle, const Core::Value& magnitude);

    virtual ~Sine();

private:
    friend class Core::Inspector;

    virtual void Accept(Core::Inspector& inspector);

    Sine(){}

    virtual Sine* Copy(void) const;

    virtual float Execute(const float seconds);

    virtual bool IsFinished(void) const;


    Core::Value* angle; /// inspect
    Core::Value* magnitude; /// inspect
};
//-----------------------------------------------------------------------------
}//Commands
